package midi.notation.util;

import midi.basic.Beat;
import midi.basic.Mode;
import midi.basic.Pitch;
import midi.basic.Speed;
import midi.midi.IO.MidiFileIO;
import midi.midi.MThd.MThd;
import midi.midi.MThd.MidiType;
import midi.midi.MThd.TickTime;
import midi.midi.MTrk.MTrk;
import midi.midi.Message.Event.FinalEvent;
import midi.midi.Message.Event.KeyPressEvent;
import midi.midi.Message.Message;
import midi.midi.MidiFile;
import midi.notation.Book;
import midi.notation.Note;
import midi.notation.Part;
import midi.notation.Section;
import midi.theory.PitchUtil;

import java.io.BufferedReader;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.List;

/**
 * 乐谱工具
 */
public class BookUtil {
    //把Book数据结构写出到midi文件
    public static void toMidiFile(Book book, String fileName) {
        MidiFile midiFile = new MidiFile();
        //MThd头部
        MThd mThd = new MThd();
        //头部
        {

            //设置基本一个四分音符的tick数目，速度越快，tick就越大
            if (book.getSpeedList().get(0) == Speed.speed32) {
                mThd.tickTime = TickTime.Tick32;
            } else if (book.getSpeedList().get(0) == Speed.speed48) {
                mThd.tickTime = TickTime.Tick48;
            } else if (book.getSpeedList().get(0) == Speed.speed64) {
                mThd.tickTime = TickTime.Tick64;
            } else if (book.getSpeedList().get(0) == Speed.speed80) {
                mThd.tickTime = TickTime.Tick80;
            } else if (book.getSpeedList().get(0) == Speed.speed96) {
                mThd.tickTime = TickTime.Tick96;
            } else if (book.getSpeedList().get(0) == Speed.speed112) {
                mThd.tickTime = TickTime.Tick112;
            }
            //设置midi文件类型是多音轨同步
            mThd.midiType = MidiType.Type01;
            //设置音轨数目
            mThd.MtrkNum = book.getPartList().size();
            midiFile.setmThd(mThd);
        }
        //写出所有音轨
        {
            //声部
            for (Part part : book.getPartList()) {
                //一条音轨
                MTrk mTrk = new MTrk();
                //依次读取小节
                int tick = 0;
                for (Section section : part.getSectionList()) {
                    //记号
                    for (Note note : section.getNoteList()) {
                        if (note.isRestingNote) {
                            //如果是休止符号
                            //把上一个休止的时间加上这次的时间
                            tick = (int) (112 * 4 * note.duration.getValue())+tick;
                        } else {
                            //音和速度
                            List<Pitch> pitchList = note.pitchList;
                            Speed speed = book.getSpeedList().get(0);
                            //对乐谱的调号进行转调
                            for (int i = 0; i <= pitchList.size() - 1; i++) {
                                PitchUtil.changeMode(pitchList.get(i), book.getModeList().get(0));
                            }
                            //变化了调式
                            if (section.getHasModeChanged()) {
                                //进行转调
                                for (int i = 0; i <= pitchList.size() - 1; i++) {
                                    PitchUtil.changeMode(pitchList.get(i), section.getMode());
                                }
                            }
                            //变化了乐谱速度
                            if (section.getHasSpeedChanged()) {
                                speed = section.getSpeed();
                            }
                            //mid系统按下所有音的键
                            for (int i = 0; i <= pitchList.size() - 1; i++) {
                                Message message = new Message();
                                if (i == 0) {
                                    message.tick = tick;
                                    //相对全音符的时间长度，一个标准全音符是112*4
                                    tick = (int) (112 * 4 * note.duration.getValue());
                                } else {
                                    message.tick = 0;
                                }
                                message.event = new KeyPressEvent(1, PitchUtil.getVoiceId(pitchList.get(i)), note.velocity);
                                mTrk.addMessage(message);
                            }

                        }
                    }
                }
                //结束事件
                Message message1 = new Message();
                message1.tick = tick;
                message1.event = new FinalEvent();
                mTrk.addMessage(message1);
                //添加到轨道中
                midiFile.addMTrk(mTrk);
            }
        }
        //保存到磁盘
        try {
            //写出文件
            FileOutputStream fileOutputStream = new FileOutputStream(fileName);
            MidiFileIO.write(fileOutputStream, midiFile);
            fileOutputStream.flush();
            fileOutputStream.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static Book fromPianoFile(String fileName) {
        try {
            Book book = null;
            FileReader fileReader = new FileReader(fileName);
            BufferedReader bufferedReader = new BufferedReader(fileReader);
            //当前行状态
            int state = 0;
            String beginName = "";
            Part currentPart = null;
            Section currentSection = null;
            Object[] objects = bufferedReader.lines().toArray();
            //逐一读取文件中每一行
            for (Object obj : objects) {
                String str = (String) obj;
                System.out.println(str);
                str = str.trim();
                if (str.startsWith(".") || str.length() == 0) {
                    //跳过注释行或空行
                    continue;
                }
                //识别关键字
                if (str.contains("declare")) {
                    //declare行
                    state = 1;
                    book = new Book();

                } else if (str.contains("begin")) {
                    //begin行   ,新建part
                    state = 2;
                    beginName = str.trim().split(" ")[1].trim().split("=")[1];
                    currentPart = new Part();
                    currentPart.setPartName(beginName);

                } else if (str.contains("end")) {
                    //end行,加入part
                    state = 3;
                    book.addPart(currentPart);
                    //清空part
                    currentPart = new Part();

                } else {
                    //declare
                    if (state == 1) {
                        //declare头部
                        if (str.contains("name")) {
                            book.setName(str.split("=")[1].trim());
                        } else if (str.contains("dateTime")) {

                        } else if (str.contains("author")) {

                        } else if (str.contains("editor")) {

                        } else if (str.contains("modeList")) {
                            List<Mode> modeList = new ArrayList<>();
                            str = str.split("=")[1].split("\\[")[1];
                            str = str.substring(0, str.length() - 1);
                            for (String str1 : str.split(",")) {
                                modeList.add(Mode.fromString(str1));
                            }
                            book.setModeList(modeList);
                        } else if (str.contains("beatList")) {
                            List<Beat> beatList = new ArrayList<>();
                            str = str.split("=")[1].split("\\[")[1];
                            str = str.substring(0, str.length() - 1);
                            for (String str1 : str.split(",")) {
                                beatList.add(Beat.formString(str1));
                            }
                            book.setBeatList(beatList);

                        } else if (str.contains("speed")) {
                            List<Speed> speedList = new ArrayList<>();
                            str = str.split("=")[1].split("\\[")[1];
                            str = str.substring(0, str.length() - 1);
                            for (String str1 : str.split(",")) {
                                speedList.add(Speed.fromSpeed(Integer.valueOf(str1)));
                            }
                            book.setSpeedList(speedList);
                        }
                    } else if (state == 2) {
                        //begin部分
                        if (str.startsWith("[")) {
                            //是新小节
                            currentSection = new Section();
                            str = str.substring(1, str.length() - 1);
                            for (String str2 : str.split(",")) {
                                if (str2.startsWith("id")) {
                                    currentSection.setId(Integer.valueOf(str2.split("=")[1]));
                                } else if (str2.startsWith("mode")) {
                                    currentSection.setHasModeChanged(true);
                                    currentSection.setMode(Mode.fromString(str2.split("=")[1]));
                                } else if (str2.startsWith("speed")) {
                                    currentSection.setHasSpeedChanged(true);
                                    currentSection.setSpeed(Speed.fromSpeed(Integer.valueOf(str2.split("=")[1])));
                                } else if (str2.startsWith("beat")) {
                                    currentSection.setHasBeatChanged(true);
                                    currentSection.setBeat(Beat.formString(str2.split("=")[1]));
                                }
                            }
                            currentPart.addSection(currentSection);

                        } else {
                            //是音符记号
                            currentSection.addNote(NoteUtil.fromNoteString(str.trim()));
                        }
                    }

                }
            }
            //关闭流
            bufferedReader.close();
            fileReader.close();
            return book;
        } catch (Exception e) {
            System.out.println("文件不存在或语法错误！");
            return null;
        }
    }
}
